﻿using System;
using System.Buffers.Binary;
using System.Text;

namespace WholeSlideImageLibrary.Formats.OptimizedPyramidTiledImage
{
#pragma warning disable CA1815 // 重写值类型上的 Equals 和相等运算符
    public readonly struct FileAssociatedImageItem
#pragma warning restore CA1815 // 重写值类型上的 Equals 和相等运算符
    {
        public FileAssociatedImageItem(string name, int width, int height, long offset, int length, string format, long totalFileSize)
        {
            Name = name;
            Width = width;
            Height = height;
            Offset = offset < 0 ? offset + totalFileSize : offset;
            Format = format;
            Length = length;
        }

        public string Name { get; }
        public int Width { get; }
        public int Height { get; }
        public long Offset { get; }
        public int Length { get; }
        public string Format { get; }

        public int GetSize()
        {
            string name = Name ?? string.Empty;
            return 25 + Encoding.UTF8.GetByteCount(name);
        }

        public static bool TryParse(ReadOnlySpan<byte> buffer, long totalFileSize, out FileAssociatedImageItem associatedImageItem, out int bytesConsumed)
        {
            int consumed = 0;
            if (buffer.IsEmpty)
            {
                goto Failure;
            }
            int length = buffer[0];
            buffer = buffer.Slice(1);
            consumed++;
            if (buffer.Length < length)
            {
                goto Failure;
            }
            string name = EncodingHelper.DecodeUtf8(buffer.Slice(0, length));
            buffer = buffer.Slice(length);
            consumed += length;
            if (buffer.Length < 24)
            {
                goto Failure;
            }
            consumed += 24;
            associatedImageItem = new FileAssociatedImageItem(
                name,
                width: BinaryPrimitives.ReadInt32LittleEndian(buffer),
                height: BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(4)),
                offset: BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(8)),
                length: BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(16)),
                format: EncodingHelper.DecodeUtf8(buffer.Slice(20, 4)),
                totalFileSize: totalFileSize);
            bytesConsumed = consumed;
            return true;
        Failure:
            associatedImageItem = default;
            bytesConsumed = consumed;
            return false;
        }

        public bool TryWriteTo(Span<byte> destination, out int bytesWritten)
        {
            int written = 0;
            if (destination.IsEmpty)
            {
                goto Failure;
            }
            string name = Name ?? string.Empty;
            int length = Encoding.UTF8.GetByteCount(name);
            if (length > 255)
            {
                goto Failure;
            }
            destination[0] = (byte)length;
            destination = destination.Slice(1);
            written++;

            if (destination.Length < length)
            {
                goto Failure;
            }
            EncodingHelper.EncodeUtf8(name, destination.Slice(0, length));
            destination = destination.Slice(length);
            written += length;

            if (destination.Length < 24)
            {
                goto Failure;
            }

            BinaryPrimitives.WriteInt32LittleEndian(destination, Width);
            BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4), Height);
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(8), Offset);
            BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(16), Length);
            written += 20;

            length = Encoding.UTF8.GetByteCount(Format);
            if (length != 4)
            {
                goto Failure;
            }
            EncodingHelper.EncodeUtf8(Format, destination.Slice(20, 4));
            written += 4;

            bytesWritten = written;
            return true;

        Failure:
            bytesWritten = written;
            return false;
        }
    }
}
